/*!
    \copyright  Copyright (c) 2023 - 2025 Qualcomm Technologies International, Ltd.
                All Rights Reserved.
                Qualcomm Technologies International, Ltd. Confidential and Proprietary.
    \file
    \ingroup    mirror_profile
    \brief      The interface implementation for background unicast handler for Mirror Profile
*/


#include "mirror_profile_signalling.h"
#include "mirror_profile_unicast_background_data.h"
#include "mirror_profile.h"

#if (defined(INCLUDE_MIRRORING) && defined(ENABLE_LE_MULTIPOINT_BACKGROUND_CIS_SUPPORT))
#include "mirror_profile_voice_source.h"
#include "mirror_profile_unicast_background_handler.h"

#include "gatt_connect.h"
#include <stdlib.h>
#include <panic.h>
#include "system_clock.h"
#include "gatt.h"
#include "csr_bt_cm_lib.h"
#include "vm.h"
#include "audio_startup.h"
#include "mdm_prim.h"
#include "mirror_profile_mdm_prim.h"



static bool mirrorProfile_IsBackgroundSessionStable(mirror_profile_lea_unicast_t *bkg_src)
{
    bool is_stable = FALSE;

    switch (bkg_src->peer_cis_state)
    {
        case MIRROR_PROFILE_CIS_SUB_STATE_IDLE: /* Fall through */
        case MIRROR_PROFILE_CIS_SUB_STATE_READY_TO_CONNECT: /* Fall through */
        case MIRROR_PROFILE_CIS_SUB_STATE_CONNECTED: /* Fall through */
        {
            switch (bkg_src->own_cis_state)
            {
                case MIRROR_PROFILE_CIS_SUB_STATE_IDLE:
                case MIRROR_PROFILE_CIS_SUB_STATE_CONNECTED:
                case MIRROR_PROFILE_CIS_SUB_STATE_READY_TO_CONNECT:
                {
                    is_stable = TRUE;
                }
                break;

                default:
                break;
            }
        }
        break;

        default:
        break;
    }

    MIRROR_LOG("mirrorProfile_IsBackgroundSessionStable peer enum:mirror_profile_cis_sub_state_t:%d own enum:mirror_profile_cis_sub_state_t:%d stable %d",
                bkg_src->peer_cis_state, bkg_src->own_cis_state, is_stable);

    return is_stable;
}

/*! \brief Updates mirror_profile background transition lock */
static void mirrorProfile_UpdateBackgroundTransitionLock(mirror_profile_lea_unicast_t *bkg_unicast_state)
{
    /* Clear the transition lock if the session is stable, else set the transition lock */
    if (mirrorProfile_IsBackgroundSessionStable(bkg_unicast_state))
    {
        bkg_unicast_state->background_transition_lock = FALSE;
    }
    else
    {
        bkg_unicast_state->background_transition_lock = TRUE;
    }
}

/*! \brief Sets the background(peer/own) cis state and updates the transition lock internally */
static void mirrorProfile_SetBackgroundCisState(mirror_profile_lea_unicast_t *bkg_src, mirror_profile_cis_sub_state_t new_state, bool is_own)
{
    mirror_profile_cis_sub_state_t old_state = is_own ? bkg_src->own_cis_state : bkg_src->peer_cis_state;

    DEBUG_LOG_INFO("mirrorProfile_SetBackgroundCisState is_own: %u enum:mirror_profile_cis_sub_state_t:%d to enum:mirror_profile_cis_sub_state_t:%d", is_own, old_state, new_state);

    if (is_own)
    {
        bkg_src->own_cis_state = new_state;
    }
    else
    {
        bkg_src->peer_cis_state = new_state;
    }

    if (MirrorProfile_IsPrimary())
    {
        /* Update the transition lock */
        mirrorProfile_UpdateBackgroundTransitionLock(bkg_src);
    }
}

static void mirrorProfile_ResetBackgroundUnicastState(mirror_profile_lea_unicast_t *bkg_unicast_state)
{
    /* Both the CIS State are idle. Reset the background unicast state */
    MirrorProfile_ResetUnicastState(bkg_unicast_state);

    MirrorProfile_SetDelayKick();
    MessageSend(MirrorProfile_GetTask(), MIRROR_INTERNAL_KICK_TARGET_STATE, NULL);
}

/* Function that helps to delegate the CIS for a background unicast session */
static void mirrorProfile_MirrorBackgroundCisCreateReq(mirror_profile_lea_unicast_t *unicast,
                                                       hci_connection_handle_t cis_handle,
                                                       cis_ownership_t cis_ownership)
{
    UNUSED(unicast);
    bdaddr bd_addr;

    MAKE_MDM_PRIM_T(MDM_LE_CIS_CREATE_REQ);

    MIRROR_LOG("mirrorProfile_MirrorBackgroundCisCreateReq handle: 0x%x, ownership: %d, time: %d", cis_handle, cis_ownership, (uint16)VmGetClock());

    MirrorPioSet();
    appDeviceGetSecondaryBdAddr(&bd_addr);
    BdaddrConvertVmToBluestack(&prim->secondary_bd_addr.addrt.addr, &bd_addr);
    prim->secondary_bd_addr.addrt.type = TBDADDR_PUBLIC;
    prim->secondary_bd_addr.tp_type = BREDR_ACL;
    prim->cis_handle = cis_handle;
    prim->cis_ownership = cis_ownership;

    VmSendMdmPrim(prim);
    MirrorPioClr();
}

/* Function that processes the BT CIS Connect indication intended for a background session */
static bool mirrorProfile_BackgroundUnicastCisConnectedInd(const LE_AUDIO_UNICAST_CIS_CONNECTED_T *message)
{
    bool status = FALSE;
    cis_ownership_t cis_ownership;
    mirror_profile_lea_unicast_t *bkg_unicast_state = NULL;
    mirror_profile_cis_sub_state_t next_cis_state = MirrorProfile_IsAudioSyncL2capConnected() ?
                                                    MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING :
                                                    MIRROR_PROFILE_CIS_SUB_STATE_READY_TO_CONNECT;

    /* Retrieve the background unicast state information for this CIS */
    bkg_unicast_state = MirrorProfile_GetBackgroundUnicastEntryByCid(message->cid);

    if (bkg_unicast_state != NULL)
    {
        if (Multidevice_IsSameAsOurSide(message->side))
        {
            mirrorProfile_SetBackgroundCisState(bkg_unicast_state, next_cis_state, TRUE);
            bkg_unicast_state->own_cis_handle = message->cis_handle;
            bkg_unicast_state->own_cis_dir = message->cis_dir;
            cis_ownership = CIS_NO_DELEGATE_NO_MIRRORING_SECONDARY;
        }
        else
        {
            mirrorProfile_SetBackgroundCisState(bkg_unicast_state, next_cis_state, FALSE);
            bkg_unicast_state->peer_cis_handle = message->cis_handle;
            bkg_unicast_state->peer_cis_dir = message->cis_dir;
            cis_ownership = CIS_DELEGATE_SECONDARY;
        }

        /* Start the delegation request for the background session only when the peer is connected */
        if (next_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING)
        {
            mirrorProfile_MirrorBackgroundCisCreateReq(bkg_unicast_state, message->cis_handle, cis_ownership);
        }
        else
        {
            /* Mirror profile not connected, send status immediately */
            LeUnicastManager_CisMirrorStatus(message->cis_handle, FALSE);
        }

        /* Set the status to TRUE, so the LE_AUDIO_UNICAST_CIS_CONNECTED_T is not sent to the foreground session */
        status = TRUE;

        MIRROR_LOG("mirrorProfile_BackgroundUnicastCisConnectedInd for (cis: %d, side: %d, handle: 0x%0x), status: %d, time: %d",
                   message->cis_id, message->side,  message->cis_handle, Multidevice_IsSameAsOurSide(message->side), SystemClockGetTimerTime());
    }

    return status;
}

static mirror_profile_cis_sub_state_t mirrorProfile_BackgroundUpdateCisStateForDisconnectReq(mirror_profile_cis_sub_state_t cis_state)
{
    switch (cis_state)
    {
        case MIRROR_PROFILE_CIS_SUB_STATE_READY_TO_CONNECT:
            /* Remote has terminated the CIS, but delegation is not yet done.Set the state to Idle */
            cis_state = MIRROR_PROFILE_CIS_SUB_STATE_IDLE;
            break;

        case MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING:
            /* Remote has terminated the CIS, while the delegation is in progress.Set the state to "Disconnect on Connecting" */
            cis_state = MIRROR_PROFILE_CIS_SUB_STATE_DISCONNECT_ON_CONNECTING;
        break;

        case MIRROR_PROFILE_CIS_SUB_STATE_CONNECTED:
            /* Remote has terminated the CIS, but delegation is not brought down yet.Set the state to disconnecting */
            cis_state = MIRROR_PROFILE_CIS_SUB_STATE_DISCONNECTING;
        break;

        default:
            /* Not expected to be any these states while recieving disconnect-ind */
            break;
    }

    return cis_state;
}

/* Function that processes the BT CIS disconnect indication intended for a background session */
static bool mirrorProfile_BackgroundUnicastCisDisconnectedInd(const LE_AUDIO_UNICAST_CIS_DISCONNECTED_T *message)
{
    bool status = FALSE;
    mirror_profile_lea_unicast_t *bkg_unicast_state = NULL;
    mirror_profile_cis_sub_state_t cis_sub_state;

    /* Retrieve the background unicast state for the CIS disconnect indication */
    bkg_unicast_state = MirrorProfile_GetBackgroundUnicastEntryByCid(message->cid);

    if (bkg_unicast_state != NULL)
    {
        if (Multidevice_IsSameAsOurSide(message->side))
        {
            cis_sub_state = mirrorProfile_BackgroundUpdateCisStateForDisconnectReq(bkg_unicast_state->own_cis_state);
            mirrorProfile_SetBackgroundCisState(bkg_unicast_state, cis_sub_state, TRUE);

            if (bkg_unicast_state->own_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_IDLE)
            {
                bkg_unicast_state->own_cis_handle = MIRROR_PROFILE_CONNECTION_HANDLE_INVALID;
            }
        }

        if (Multidevice_IsSameAsPairSide(message->side))
        {
            cis_sub_state = mirrorProfile_BackgroundUpdateCisStateForDisconnectReq(bkg_unicast_state->peer_cis_state);
            mirrorProfile_SetBackgroundCisState(bkg_unicast_state, cis_sub_state, FALSE);

            if (bkg_unicast_state->peer_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_IDLE)
            {
                bkg_unicast_state->peer_cis_handle = MIRROR_PROFILE_CONNECTION_HANDLE_INVALID;
            }
        }

        if (MirrorProfile_IsBothCisInIdleState(bkg_unicast_state))
        {
            {
                /* Both the CIS state are idle. Send a background audio config clear request to secondary */
                MirrorProfile_SendBackgroundUnicastConfigDataClear(&bkg_unicast_state->bd_addr);

                /* Reset the unicast state */
                mirrorProfile_ResetBackgroundUnicastState(bkg_unicast_state);
            }
        }

        /* Set the status to TRUE, so the LE_AUDIO_UNICAST_CIS_DISCONNECTED_T is not sent to the foreground session */
        status = TRUE;

        MIRROR_LOG("mirrorProfile_BackgroundUnicastCisDisconnectedInd for (cis: %d, side: %d, handle: 0x%0x), is_our_side: %d",
                   message->cis_id, message->side, message->cis_handle, Multidevice_IsSameAsOurSide(message->side));
    }

    return status;
}

/* Function which checks whether to do any background session cleanup when a unicast session ends */
static bool mirrorProfile_BackgroundHandleUnicastDisabled(const LE_AUDIO_UNICAST_DISABLED_IND_T *message)
{
    bool status = FALSE;
    mirror_profile_lea_unicast_t *bkg_unicast_state = NULL;

    /* Retrieve the background unicast state(if any) when remote is tearing down the session */
    bkg_unicast_state = MirrorProfile_GetBackgroundUnicastEntryByCid(message->cid);

    if (bkg_unicast_state != NULL)
    {
        status = TRUE;

        if (MirrorProfile_IsBothCisInIdleState(bkg_unicast_state))
        {
            /* Both the CIS state are idle. Send a background audio config clear request to secondary */
            MirrorProfile_SendBackgroundUnicastConfigDataClear(&bkg_unicast_state->bd_addr);

            /* Reset the unicast state */
            MirrorProfile_ResetUnicastState(bkg_unicast_state);

            MIRROR_LOG("mirrorProfile_BackgroundUnicastUnicastDisabledInd Stale Entry Cleared for lap 0x%04x",
                        bkg_unicast_state->bd_addr.lap);
        }
    }

    return status;
}

/* Iterates through the background unicast list and chooses a probable entry to which a CIS delegation
 * failure should be mapped and cleaned up.
 */
static void mirrorProfile_HandleBackgroundCisDelegationFailure(void)
{
    int index;
    mirror_profile_background_unicast_list_t *bkg_lea_unicast = MirrorProfile_GetBackroundUnicastList();

    for (index = 0; index < MIRROR_PROFILE_BACKGROUND_UNICAST_LIST_MAX_SIZE; index++)
    {
        if (bkg_lea_unicast->config[index].own_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_DISCONNECT_ON_CONNECTING ||
            bkg_lea_unicast->config[index].own_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING)
        {
            mirrorProfile_SetBackgroundCisState(&bkg_lea_unicast->config[index], MIRROR_PROFILE_CIS_SUB_STATE_IDLE, TRUE);
        }
        else if (bkg_lea_unicast->config[index].peer_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_DISCONNECT_ON_CONNECTING ||
                 bkg_lea_unicast->config[index].peer_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING)
        {
            mirrorProfile_SetBackgroundCisState(&bkg_lea_unicast->config[index], MIRROR_PROFILE_CIS_SUB_STATE_IDLE, FALSE);
        }
        else
        {
            continue;
        }

        if (MirrorProfile_IsBothCisInIdleState(&bkg_lea_unicast->config[index]))
        {
           /* The background CIS delegation failed. The Secondary might already be having the configs
            * Send a background audio config clear request to secondary
            */
           MirrorProfile_SendBackgroundUnicastConfigDataClear(&bkg_lea_unicast->config[index].bd_addr);

           /* Reset the unicast state */
           mirrorProfile_ResetBackgroundUnicastState(&bkg_lea_unicast->config[index]);
        }

        break;
    }
}
/*! \brief Determines CIS next state on primary based on current and CIS delegate state

    \param state Current state
    \param is_success Is CIS delegation/mirroring/no-mirroing is successful

    \return Next CIS state
*/
static mirror_profile_cis_sub_state_t mirrorProfile_BackgroundUnicastGetCisNextStateOnPrimary(mirror_profile_cis_sub_state_t state,
                                                                                              bool is_success)
{
    mirror_profile_cis_sub_state_t next_state = state;

    switch (state)
    {
        case MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING:
            next_state = is_success ? MIRROR_PROFILE_CIS_SUB_STATE_CONNECTED :
                                      MIRROR_PROFILE_CIS_SUB_STATE_READY_TO_CONNECT;
            break;

        case MIRROR_PROFILE_CIS_SUB_STATE_DISCONNECTING:
        case MIRROR_PROFILE_CIS_SUB_STATE_DISCONNECT_ON_CONNECTING:
            next_state = is_success ? MIRROR_PROFILE_CIS_SUB_STATE_DISCONNECTING :
                                      MIRROR_PROFILE_CIS_SUB_STATE_IDLE;
            break;

        default:
            MIRROR_LOG("mirrorProfile_BackgroundUnicastGetCisNextStateOnPrimary bad CIS state enum:mirror_profile_cis_sub_state_t:%d", state);
            assert(0);
            break;
    }

    return next_state;
}

/* Function that handles a MDM_LE_CIS_CREATE_CFM_T for a background unicast session */
static bool mirrorProfile_BackgroundUnicastHandleCisCreateCfm(const MDM_LE_CIS_CREATE_CFM_T *cfm)
{
    bool status = FALSE;
    bool cis_status = cfm->status == HCI_SUCCESS;
    hci_connection_handle_t cis_handle = cfm->cis_handle;
    mirror_profile_lea_unicast_t *fg_unicast_state = MirrorProfile_GetLeaUnicastState();
    mirror_profile_lea_unicast_t *bkg_unicast_state = MirrorProfile_GetBackgroundUnicastEntryByCisHandle(cis_handle);
    mirror_profile_cis_sub_state_t cis_state;

    if (cfm->status != HCI_SUCCESS && bkg_unicast_state == NULL && fg_unicast_state->own_cis_handle != cis_handle && fg_unicast_state->peer_cis_handle != cis_handle)
    {
        cis_handle = MIRROR_PROFILE_CONNECTION_HANDLE_INVALID;
    }

    assert(MirrorProfile_IsPrimary());

    if (cis_handle == MIRROR_PROFILE_CONNECTION_HANDLE_INVALID)
    {
        /* CIS Handle arrived as invalid. This is a tricky situation as the Mirror CIS Create request
         * could have been placed either by foreground OR a background session. Its difficult to
         * distuinguish for which this message intends.

         * This can happen in race conditions where we have placed a mirror connect request and the remote
         * has disconnected the CIS while delegation is in progress.

         * For now check if the foreground CIS Substates are not in transient state, consume it locally
         * else push forward this message to the foreground session.
         */
        if (!MirrorProfile_IsAnyCisInConnectingState(fg_unicast_state) &&
            !MirrorProfile_IsAnyCisInDisconnectOnConnectingState(fg_unicast_state))
        {
            DEBUG_LOG_INFO("mirrorProfile_BackgroundUnicastHandleCisCreateCfm Consumed handle: 0x%x, hci_status: 0x%x",
                           cfm->cis_handle, cfm->status);

            /* The Main LEA Unicast Mirror CIS State are either idle or both connected.
             * This CIS create CFM cannot be propagated to main sm. Consume it locally.
             */
            status = TRUE;

            /* The failure is for a background session. Handle it!*/
            mirrorProfile_HandleBackgroundCisDelegationFailure();
        }
    }
    else if (bkg_unicast_state != NULL)
    {
        DEBUG_LOG_INFO("mirrorProfile_BackgroundUnicastHandleCisCreateCfm For Background handle: 0x%x, hci_status: 0x%x",
                       cfm->cis_handle, cfm->status);

        if (bkg_unicast_state->peer_cis_handle == cfm->cis_handle)
        {
            cis_state = mirrorProfile_BackgroundUnicastGetCisNextStateOnPrimary(bkg_unicast_state->peer_cis_state, cis_status);
            mirrorProfile_SetBackgroundCisState(bkg_unicast_state, cis_state, FALSE);

            LeUnicastManager_CisMirrorStatus(cfm->cis_handle, cis_status);

            if (bkg_unicast_state->peer_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_READY_TO_CONNECT &&
                MirrorProfile_IsAudioSyncL2capConnected())
            {
                /* @todo Do not retry continously. Should we retry for some iterations and if all fails we should stop?? */
                mirrorProfile_MirrorBackgroundCisCreateReq(bkg_unicast_state,
                                                           bkg_unicast_state->peer_cis_handle,
                                                           CIS_DELEGATE_SECONDARY);
                /* Set the CIS state to connecting as the CIS create request is sent again */
                mirrorProfile_SetBackgroundCisState(bkg_unicast_state, MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING, FALSE);
            }
        }

        if (bkg_unicast_state->own_cis_handle == cfm->cis_handle)
        {
            cis_state = mirrorProfile_BackgroundUnicastGetCisNextStateOnPrimary(bkg_unicast_state->own_cis_state, cis_status);
            mirrorProfile_SetBackgroundCisState(bkg_unicast_state, cis_state, TRUE);

            LeUnicastManager_CisMirrorStatus(cfm->cis_handle, cis_status);

            if (bkg_unicast_state->own_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_READY_TO_CONNECT &&
                MirrorProfile_IsAudioSyncL2capConnected())
            {
                /* @todo Do not retry continously. Should we retry for some iterations and if all fails we should stop?? */
                mirrorProfile_MirrorBackgroundCisCreateReq(bkg_unicast_state,
                                                           bkg_unicast_state->own_cis_handle,
                                                           CIS_NO_DELEGATE_NO_MIRRORING_SECONDARY);
                /* Set the CIS state to connecting as the CIS create request is sent again */
                mirrorProfile_SetBackgroundCisState(bkg_unicast_state, MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING, TRUE);
            }
        }

        if (MirrorProfile_IsBothCisInIdleState(bkg_unicast_state))
        {
            /* Both the CIS state are idle. Send a background audio config clear request to secondary */
            MirrorProfile_SendBackgroundUnicastConfigDataClear(&bkg_unicast_state->bd_addr);
            /* Both the CIS State are idle. Reset the background unicast state */
            mirrorProfile_ResetBackgroundUnicastState(bkg_unicast_state);
        }

        /* Set the status to TRUE, so the MDM_LE_CIS_CREATE_CFM_T is not sent to the foreground session */
        status = TRUE;
    }

    return status;
}

/* Function that checks whether MDM_LE_CIS_DISCONNECT_IND_T is for a background unicast session and processes accordingly */
static bool mirrorProfile_BackgroundUnicastHandleMdmCisDisconnectIndOnPrimary(const MDM_LE_CIS_DISCONNECT_IND_T *ind)
{
    bool status = FALSE;
    mirror_profile_lea_unicast_t *bkg_unicast_state = MirrorProfile_GetBackgroundUnicastEntryByCisHandle(ind->conn_handle);

    if (bkg_unicast_state != NULL)
    {
        DEBUG_LOG_INFO("mirrorProfile_BackgroundUnicastHandleMdmCisDisconnectIndOnPrimary handle: 0x%x", ind->conn_handle);

        if (bkg_unicast_state->peer_cis_handle == ind->conn_handle)
        {
           if (bkg_unicast_state->peer_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_CONNECTED)
           {
               /* Remote has not disconnected the CIS yet, but delegation is down. Set the state to "Ready to connect" */
               mirrorProfile_SetBackgroundCisState(bkg_unicast_state, MIRROR_PROFILE_CIS_SUB_STATE_READY_TO_CONNECT, FALSE);

               /* If delegation was disconnected locally, retry again */
               if (ind->reason != hci_error_oetc_user &&
                   ind->reason != hci_error_conn_timeout &&
                   ind->reason != hci_error_lmp_response_timeout)
               {
                   mirrorProfile_SetBackgroundCisState(bkg_unicast_state, MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING, FALSE);

                   /* @todo How long we have to retry? */
                   mirrorProfile_MirrorBackgroundCisCreateReq(bkg_unicast_state, bkg_unicast_state->peer_cis_handle, CIS_DELEGATE_SECONDARY);
               }
           }
           else if (bkg_unicast_state->peer_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_DISCONNECTING)
           {
              /* Remote has already disconnected the CIS. The delegation is brought down. Set the Background CIS State to "IDLE" */
               mirrorProfile_SetBackgroundCisState(bkg_unicast_state, MIRROR_PROFILE_CIS_SUB_STATE_IDLE, FALSE);
               bkg_unicast_state->peer_cis_handle = MIRROR_PROFILE_CONNECTION_HANDLE_INVALID;
           }
           else
           {
               Panic();
           }
        }

        if (bkg_unicast_state->own_cis_handle == ind->conn_handle)
        {
            if (bkg_unicast_state->own_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_CONNECTED)
            {
                /* Remote has not disconnected the CIS yet, but delegation is down. Set the state to "Ready to connect" */
                mirrorProfile_SetBackgroundCisState(bkg_unicast_state, MIRROR_PROFILE_CIS_SUB_STATE_READY_TO_CONNECT, TRUE);

                /* If delegation was disconnected locally, retry again */
                if (ind->reason != hci_error_oetc_user &&
                    ind->reason != hci_error_conn_timeout &&
                    ind->reason != hci_error_lmp_response_timeout)
                {
                    mirrorProfile_SetBackgroundCisState(bkg_unicast_state, MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING, TRUE);

                    /* @todo How long we have to retry? */
                    mirrorProfile_MirrorBackgroundCisCreateReq(bkg_unicast_state, bkg_unicast_state->own_cis_handle, CIS_NO_DELEGATE_NO_MIRRORING_SECONDARY);
                }
            }
            else if (bkg_unicast_state->own_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_DISCONNECTING)
            {
                /* Remote has already disconnected the CIS. The delegation us also brought down. Set the Background CIS State to "IDLE" */
                mirrorProfile_SetBackgroundCisState(bkg_unicast_state, MIRROR_PROFILE_CIS_SUB_STATE_IDLE, TRUE);
                bkg_unicast_state->own_cis_handle = MIRROR_PROFILE_CONNECTION_HANDLE_INVALID;
            }
            else
            {
                Panic();
            }
        }

        if (MirrorProfile_IsBothCisInIdleState(bkg_unicast_state))
        {
            /* Both the CIS state are idle. Send a background audio config clear request to secondary */
            MirrorProfile_SendBackgroundUnicastConfigDataClear(&bkg_unicast_state->bd_addr);
            /* Both the CIS State are idle. Reset the background unicast state */
            mirrorProfile_ResetBackgroundUnicastState(bkg_unicast_state);
        }

        /* Set the status to TRUE, so the MDM_LE_CIS_DISCONNECT_IND_T is not sent to the foreground session */
        status = TRUE;
    }

    return status;
}

/* Function that checks whether MDM_LE_CIS_DISCONNECT_IND_T is for a background unicast session in secondary and processes accordingly */
static bool mirrorProfile_BackgroundUnicastHandleMdmCisDisconnectIndOnSecondary(const MDM_LE_CIS_DISCONNECT_IND_T *ind)
{
    bool status = FALSE;
    mirror_profile_lea_unicast_t *bkg_unicast_state = MirrorProfile_GetBackgroundUnicastEntryByCisHandle(ind->conn_handle);

    if (bkg_unicast_state != NULL)
    {
        DEBUG_LOG_INFO("mirrorProfile_BackgroundUnicastHandleMdmCisDisconnectIndOnSecondary handle: 0x%x", ind->conn_handle);

        /* The MDM_LE_CIS_DISCONNECT_IND_T belongs to a background session */
        if (bkg_unicast_state->peer_cis_handle == ind->conn_handle)
        {
            mirrorProfile_SetBackgroundCisState(bkg_unicast_state, MIRROR_PROFILE_CIS_SUB_STATE_IDLE, FALSE);
        }

        if (bkg_unicast_state->own_cis_handle == ind->conn_handle)
        {
            mirrorProfile_SetBackgroundCisState(bkg_unicast_state, MIRROR_PROFILE_CIS_SUB_STATE_IDLE, TRUE);
        }

        if (MirrorProfile_IsBothCisInIdleState(bkg_unicast_state))
        {
            /* If both states are idle, reset the background unicast session information */
            MirrorProfile_ResetUnicastState(bkg_unicast_state);
        }

        /* Set the status to TRUE, so the MDM_LE_CIS_DISCONNECT_IND_T is not sent to the foreground session */
        status = TRUE;
    }

    return status;
}

static mirror_profile_lea_unicast_t* mirrorProfile_GetBackgroundUnicastEntryByIdentityOrPublicAddr(const bdaddr *addr, const bdaddr *ia_addr)
{
    mirror_profile_lea_unicast_t *unicast_entry = NULL;

    unicast_entry = MirrorProfile_GetBackgroundUnicastEntryByAddr(addr);

    /* The unicast entry was not found using public address, try to find by random address */
    if (unicast_entry == NULL)
    {
        unicast_entry = MirrorProfile_GetBackgroundUnicastEntryByAddr(ia_addr);
    }

    return unicast_entry;
}

static bool mirrorProfile_IsCisCreateIndRcvdBeforeBkgTransistionReq(bdaddr *mirror_address)
{
    mirror_profile_lea_unicast_t *lea_unicast = MirrorProfile_GetLeaUnicastState();

    return !MirrorProfile_IsSecondaryInCisAcceptState() && BdaddrIsSame(&lea_unicast->bd_addr, mirror_address);
}

/* Check and handle MDM_LE_CIS_CREATE_IND_T if it belongs to a background unicast session */
static bool mirrorProfile_BackgroundUnicastHandleCisCreateInd(const MDM_LE_CIS_CREATE_IND_T *ind)
{
    bdaddr addr;
    mirror_profile_lea_unicast_t *bkg_unicast_state = NULL;
    bool status = FALSE;
    hci_connection_handle_t cis_handle = ind->cis_handle;
    mirror_profile_lea_unicast_t *lea_unicast = MirrorProfile_GetLeaUnicastState();
    bool is_cis_create_ind_recvd_before_bg_transition_req;

    assert(!MirrorProfile_IsPrimary());

    /* Get the delegated/mirrored device address for which the CIS got created */
    BdaddrConvertBluestackToVm(&addr, &ind->mirror_bd_addr.addrt.addr);

    /* Check if there is a entry in background unicast list matching to this address or IA */
    bkg_unicast_state = mirrorProfile_GetBackgroundUnicastEntryByIdentityOrPublicAddr(&addr, &addr);
    is_cis_create_ind_recvd_before_bg_transition_req = mirrorProfile_IsCisCreateIndRcvdBeforeBkgTransistionReq(&addr);

    if (bkg_unicast_state == NULL &&
        ((!BdaddrIsSame(&lea_unicast->bd_addr, &addr) && !BdaddrIsSame(&lea_unicast->ia_addr, &addr)) ||
         is_cis_create_ind_recvd_before_bg_transition_req) &&
        ind->status == HCI_SUCCESS)
    {
        /* This MDM Cis Create Indication arrived earlier, so we don't know whether it belongs to foreground
         * or background. For now preserve this CIS in the background.
         */
        bkg_unicast_state = MirrorProfile_GetNextBackgroundUnicastFreeEntry();

        /* 1) A unicast session is being setup in the FG with configs sent to secondary.
         * 2) In primary the session lost focus and went to background and transition request placed to secondary.
         * 3) The Delegation is done in the background now and CIS Create IND arrived earlier to secondary before
         *    transition request to background arrived in secondary.
         *  Check if Main mirror profile state for foreground is non CIS related, that means MDM Cis Create Indication
         *  is for background session. Transition the FG session at secondary into new background entry and handle CIS create Ind.
         */
        if (is_cis_create_ind_recvd_before_bg_transition_req)
        {
            memcpy(bkg_unicast_state, lea_unicast, sizeof(mirror_profile_lea_unicast_t));
            bkg_unicast_state->audio_config.is_foreground = FALSE;
            MirrorProfile_ResetUnicastState(lea_unicast);
        }

        /* Extract the address from CIS Create Indication handle and preserve it in LEA Unicast */
        bkg_unicast_state->bd_addr.lap = addr.lap;
        bkg_unicast_state->bd_addr.nap = addr.nap;
        bkg_unicast_state->bd_addr.uap = addr.uap;

        /* Copy the address(Either RA or IA) into IA as well.
         * Later when configs are received, this will get 
         * correctly updated 
         */
        bkg_unicast_state->ia_addr.lap = addr.lap;
        bkg_unicast_state->ia_addr.nap = addr.nap;
        bkg_unicast_state->ia_addr.uap = addr.uap;
    }

    if (bkg_unicast_state != NULL)
    {
        DEBUG_LOG_INFO("mirrorProfile_BackgroundUnicastHandleCisCreateInd handle: 0x%x, hci_status: 0x%x", ind->cis_handle, ind->status);

        if (ind->status != HCI_SUCCESS)
        {
            /* This is for background session, but resulted in a failure. Consume this message and return TRUE */
            return TRUE;
        }

        /* This CIS got created for the background session. Preserve it in the Background state. */
        if (ind->cis_ownership == CIS_NO_DELEGATE_NO_MIRRORING_SECONDARY)
        {
            bkg_unicast_state->peer_cis_handle = cis_handle;
            bkg_unicast_state->peer_cis_dir = ind->cis_params.bn_c_to_p != 0 ? LE_AUDIO_ISO_DIRECTION_DL : 0;
            bkg_unicast_state->peer_cis_dir |= ind->cis_params.bn_p_to_c != 0 ? LE_AUDIO_ISO_DIRECTION_UL : 0;
            bkg_unicast_state->peer_cis_state = MIRROR_PROFILE_CIS_SUB_STATE_CONNECTED;
        }
        else
        {
            bkg_unicast_state->own_cis_handle = cis_handle;
            bkg_unicast_state->own_cis_dir = ind->cis_params.bn_c_to_p != 0 ? LE_AUDIO_ISO_DIRECTION_DL : 0;
            bkg_unicast_state->own_cis_dir |= ind->cis_params.bn_p_to_c != 0 ? LE_AUDIO_ISO_DIRECTION_UL : 0;
            bkg_unicast_state->own_cis_state = MIRROR_PROFILE_CIS_SUB_STATE_CONNECTED;
        }

        /* Set the status to TRUE, so the MDM_LE_CIS_CREATE_IND_T is not sent to the foreground session */
        status = TRUE;
    }

    return status;
}

/* function that handles all the Bluestack MDM prims(unicast) and checks if it is intended for a background session */
static bool mirrorProfile_BackgroundUnicastHandleBlueStackPrim(const MDM_UPRIM_T *uprim)
{
    bool status = FALSE;

    switch (uprim->type)
    {
        case MDM_LE_CIS_CREATE_CFM:
        {
            status = mirrorProfile_BackgroundUnicastHandleCisCreateCfm((const MDM_LE_CIS_CREATE_CFM_T *)uprim);
        }
        break;

        case MDM_LE_CIS_CREATE_IND:
        {
            status = mirrorProfile_BackgroundUnicastHandleCisCreateInd((const MDM_LE_CIS_CREATE_IND_T *)uprim);
        }
        break;

        case MDM_LE_CIS_DISCONNECT_IND:
        {
            if (MirrorProfile_IsPrimary())
            {
                status = mirrorProfile_BackgroundUnicastHandleMdmCisDisconnectIndOnPrimary((const MDM_LE_CIS_DISCONNECT_IND_T *)uprim);
            }
            else
            {
                status = mirrorProfile_BackgroundUnicastHandleMdmCisDisconnectIndOnSecondary((const MDM_LE_CIS_DISCONNECT_IND_T *)uprim);
            }
        }
        break;

        default:
        break;
    }

    /* A status of TRUE indicates this message is consumed by the background session.*/
    return status;
}

static void mirrorProfile_CisCreateReqOnBackgroundTransition(mirror_profile_lea_unicast_t *lea_unicast, bool for_peer_cis)
{
    mirror_profile_cis_sub_state_t cis_state;
    hci_connection_handle_t cis_handle;
    cis_ownership_t cis_ownership;

    if (for_peer_cis)
    {
        cis_state = lea_unicast->peer_cis_state;
        cis_handle = lea_unicast->peer_cis_handle;
        cis_ownership = CIS_DELEGATE_SECONDARY;
    }
    else
    {
        cis_state = lea_unicast->own_cis_state;
        cis_handle = lea_unicast->own_cis_handle;
        cis_ownership = CIS_NO_DELEGATE_NO_MIRRORING_SECONDARY;
    }

    if (cis_state == MIRROR_PROFILE_CIS_SUB_STATE_READY_TO_CONNECT && MirrorProfile_IsAudioSyncL2capConnected())
    {
        /* Remote has connected the CIS, but before Mirror could place the delegation request, the source
         * got pushed into background. Initiate a background delegation before moving to background.
         */

        mirrorProfile_MirrorBackgroundCisCreateReq(lea_unicast, cis_handle, cis_ownership);
        if (for_peer_cis)
        {
            mirrorProfile_SetBackgroundCisState(lea_unicast, MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING, FALSE);
        }
        else
        {
            mirrorProfile_SetBackgroundCisState(lea_unicast, MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING, TRUE);
        }
    }
}

/* Function that updates relay status to lower layer */
static void mirrorProfile_UpdateRelayState(mirror_profile_lea_unicast_t *lea_unicast, bool enable_relay)
{
    if (mirrorProfile_IsDualCisRelayEnabled(lea_unicast))
    {
        if (lea_unicast->own_cis_handle != MIRROR_PROFILE_CONNECTION_HANDLE_INVALID)
        {
            MirrorProfile_MirrorCisSetRelayEnable(lea_unicast->own_cis_handle, LE_RELAY_DIR_OWNER_TO_MIRROR, enable_relay);
        }
    
        if (lea_unicast->peer_cis_handle != MIRROR_PROFILE_CONNECTION_HANDLE_INVALID)
        {
            MirrorProfile_MirrorCisSetRelayEnable(lea_unicast->peer_cis_handle, LE_RELAY_DIR_OWNER_TO_MIRROR, enable_relay);
        }
    }
}

/* Function that moves a unicast source to background in the primary earbud */
static bool mirrorProfile_MoveUnicastSourceToBackground(generic_source_t source)
{
    mirror_profile_lea_unicast_t *lea_unicast = MirrorProfile_GetLeaUnicastState();
    mirror_profile_lea_unicast_t *bkg_unicast = MirrorProfile_GetBackgroundUnicastEntryBySource(source);
    bool transition_initiated = FALSE;

    /* check if the source is already present in background */
    if (bkg_unicast != NULL)
    {
        /* The session which was about to be transitioned to foreground got pushed again to background,
         * since its already in background, cancel the internal transition message if any and return
         */
        MessageCancelFirst(MirrorProfile_GetTask(), MIRROR_PROFILE_INTERNAL_TRANSITION_SOURCE_TO_FOREGROUND);
        DEBUG_LOG_INFO("mirrorProfile_MoveUnicastSourceToBackground Source Type %d already in background");
        return TRUE;
    }

    if ((source.type == source_type_voice && lea_unicast->audio_config.voice_source == source.u.voice) ||
        (source.type == source_type_audio && lea_unicast->audio_config.audio_source == source.u.audio))
    {
        /* Transition the foreground source into background state */
        mirror_profile_lea_unicast_t *bkg_lea_unicast = MirrorProfile_GetNextBackgroundUnicastFreeEntry();

        PanicNull(bkg_lea_unicast);
        *bkg_lea_unicast = *lea_unicast;
        MirrorProfile_ResetUnicastState(lea_unicast);

        mirrorProfile_UpdateRelayState(bkg_lea_unicast, LE_RELAY_DISABLE);

        /* Transition the foreground mirror profile CIS substates for background */
        mirrorProfile_CisCreateReqOnBackgroundTransition(bkg_lea_unicast, TRUE);
        mirrorProfile_CisCreateReqOnBackgroundTransition(bkg_lea_unicast, FALSE);

        /* If the background session is not stable, set the lock */
        mirrorProfile_UpdateBackgroundTransitionLock(bkg_lea_unicast);

        /* Set the foreground status to false as this source has lost foreground status */
        bkg_lea_unicast->audio_config.is_foreground = FALSE;

        transition_initiated = TRUE;

        /* Inform Secondary to transition its state to background for this source */
        MirrorProfile_SendUnicastTransitionReq(source, FALSE);

        if (MirrorProfile_GetState() == MIRROR_PROFILE_STATE_CIS_CONNECTING ||
            MirrorProfile_GetState() == MIRROR_PROFILE_STATE_CIS_DISCONNECTING ||
            MirrorProfile_GetState() == MIRROR_PROFILE_STATE_CIS_CONNECTED)
        {
            /* We did a force transition from foreground to background when the
             * Mirror is in a transient state. This can happen where Mirror was busy
             * delegating a foreground session but the focus got shifted quickly
             * to some other audio source. We have to clear the Mirror profile
             * SM lock and set the Mirror profile state to disconnected.The target
             * state will be recomputed again.
             *
             * All upcoming mirror profile MDM prims for this foreground session
             * will now be handled in the background.
             *
             * Cancel any Pending MIRROR_INTERNAL_SET_TARGET_STATE, so that earlier
             * computed target state is not set in quick transition, as MirrorProfile_SetState()
             * will again compute the target state.
             */
            MirrorProfile_SetDelayKick();
            MessageCancelFirst(MirrorProfile_GetTask(), MIRROR_INTERNAL_SET_TARGET_STATE);
            MirrorProfile_SetState(MIRROR_PROFILE_STATE_DISCONNECTED);
        }
        else if (MirrorProfile_GetTargetState() == MIRROR_PROFILE_STATE_CIS_CONNECTED &&
                 MirrorProfile_GetState() == MIRROR_PROFILE_STATE_DISCONNECTED)
        {
            /* This is a race condition, where the LEA source got focus, the target state is already 
             * computed (CIS_CONNECTED), but the mirror has not yet started the transition (Mirror 
             * profile could be busy changing the buddy link from sniff to active). In the meantime 
             * a request to transition to background has arrived. Though the source has been moved 
             * to background, message for setting of target state is still in queue since stable 
             * state was not reached. Recompute the target state again, which would clear the earlier 
             * message for setting target state and set the fresh target state according to available source.
             */
            MirrorProfile_SetTargetStateFromProfileState();
        }
    }

    DEBUG_LOG_INFO("mirrorProfile_MoveUnicastSourceToBackground Source Type %d, transition_initiated %d", source.type, transition_initiated);

    return transition_initiated;
}

bool MirrorProfile_MoveUnicastSourceToForeground(generic_source_t source)
{
    mirror_profile_lea_unicast_t *bkg_lea_unicast = MirrorProfile_GetBackgroundUnicastEntryBySource(source);
    bool transition_done = FALSE;

    if (bkg_lea_unicast != NULL)
    {
        /* The background session is not in a stable state. Retry later */
        if (bkg_lea_unicast->background_transition_lock)
        {
            MIRROR_PROFILE_INTERNAL_TRANSITION_SOURCE_TO_FOREGROUND_T * const internal_msg =
               (MIRROR_PROFILE_INTERNAL_TRANSITION_SOURCE_TO_FOREGROUND_T *) PanicUnlessMalloc(sizeof(*internal_msg));

            DEBUG_LOG_INFO("MirrorProfile_MoveUnicastSourceToForeground Not Stable, Transition later");

            internal_msg->source = source;
            MessageSendConditionally(MirrorProfile_GetTask(), MIRROR_PROFILE_INTERNAL_TRANSITION_SOURCE_TO_FOREGROUND,
                                     internal_msg, &bkg_lea_unicast->background_transition_lock);

            /* Return TRUE, as anyhow, the session will transition to foreground shortly */
            return TRUE;
        }

        if (bkg_lea_unicast->config_sent)
        {
            /* we cannot inform secondary right away as secondary could be still transitioning from
             * other mirror profile states(like transitioning from ESCO_CONNECTED, A2DP_ROUTED,
             * ACL_CONNECTED) For now record that a transition is pending, so after this configs 
             * have reached to foreground, the main Mirror SM will use this flag to initiate
             * transition request to secondary when it starts the transition towards CIS_CONNECTED
             * state
             */
            bkg_lea_unicast->transition_peer_request_pending = TRUE;
        }

        /* A background session exists for this source. Copy it to foreground */
        mirror_profile_lea_unicast_t *lea_unicast = MirrorProfile_GetLeaUnicastState();

        *lea_unicast = *bkg_lea_unicast;

        /* Set the foreground flag to TRUE as this source has regained focus */
        lea_unicast->audio_config.is_foreground = TRUE;


        /* Reset the background state */
        MirrorProfile_ResetUnicastState(bkg_lea_unicast);

        mirrorProfile_UpdateRelayState(lea_unicast, LE_RELAY_ENABLE);

        /* A transition has been initiated successfully */
        transition_done = TRUE;

        /* Ask the mirror profile to compute the target state immediately */
        MessageSend(MirrorProfile_GetTask(), MIRROR_INTERNAL_KICK_TARGET_STATE, NULL);
    }

    DEBUG_LOG_INFO("mirrorProfile_MoveUnicastSourceToForeground Source Type %d, transition_done %d", source.type, transition_done);

    return transition_done;
}

static void mirrorProfile_RemoveDataPath(uint16 cis_handle, uint8 direction_mask)
{
    Task mp_task = MirrorProfile_GetTask();

    MIRROR_LOG("mirrorProfile_RemoveDataPath: cis_handle %d cis_dir=0x%01x", cis_handle, direction_mask);

    CmIsocRemoveIsoDataPathReqSend(TrapToOxygenTask(mp_task), cis_handle, direction_mask);
}

bool MirrorProfile_BackgroundUnicastMessageHandler(MessageId id, Message message)
{
    bool unicast_msg_handled = FALSE;

    switch (id)
    {
        case LE_AUDIO_UNICAST_CIS_CONNECTED:
        {
            unicast_msg_handled = mirrorProfile_BackgroundUnicastCisConnectedInd(message);
        }
        break;

        case LE_AUDIO_UNICAST_CIS_DISCONNECTED:
        {
            unicast_msg_handled = mirrorProfile_BackgroundUnicastCisDisconnectedInd(message);
        }
        break;

        case LE_AUDIO_UNICAST_DISABLED_IND:
        {
            unicast_msg_handled = mirrorProfile_BackgroundHandleUnicastDisabled(message);
        }
        break;

        case MESSAGE_BLUESTACK_MDM_PRIM:
        {
            unicast_msg_handled = mirrorProfile_BackgroundUnicastHandleBlueStackPrim(message);
        }
        break;

        default:
        break;
    }

    return unicast_msg_handled;
}

void MirrorProfile_HandleBackgroundLeaUnicastConfig(const mirror_profile_lea_unicast_audio_conf_req_t *audio_config)
{
    bool entry_found = TRUE;
    bdaddr addr;
    bdaddr ia_addr;

    mirror_profile_lea_unicast_t *config = NULL;

    assert(!MirrorProfile_IsPrimary());

    addr.uap = audio_config->uap;
    addr.nap = audio_config->nap;
    addr.lap = audio_config->lap;

    /* Check if there is any entry with IA Address */
    ia_addr.uap = audio_config->ia_uap;
    ia_addr.nap = audio_config->ia_nap;
    ia_addr.lap = audio_config->ia_lap;

    config = mirrorProfile_GetBackgroundUnicastEntryByIdentityOrPublicAddr(&addr, &ia_addr);

    if (config == NULL)
    {
        config = MirrorProfile_GetNextBackgroundUnicastFreeEntry();
        entry_found = FALSE;
    }

    MIRROR_LOG("MirrorProfile_HandleBackgroundLeaUnicastConfig lap 0x%04x, existing entry_found: %d", audio_config->lap, entry_found);

    PanicNull(config);
    config->audio_config = *audio_config;
    config->config_rcvd = TRUE;
    config->config_sent = TRUE;

    /* Preserve the BT Address(could be RA or IA) */
    config->bd_addr.uap = audio_config->uap;
    config->bd_addr.nap = audio_config->nap;
    config->bd_addr.lap = audio_config->lap;

    /* Preserve the IA Address */
    config->ia_addr.uap = audio_config->ia_uap;
    config->ia_addr.nap = audio_config->ia_nap;
    config->ia_addr.lap = audio_config->ia_lap;

}

void MirrorProfile_ClearBackgroundLeaUnicastConfig(const mirror_profile_lea_unicast_audio_clear_req_t *ind)
{
    bdaddr addr;
    mirror_profile_lea_unicast_t* bkg_unicast_state = NULL;

    assert(!MirrorProfile_IsPrimary());

    addr.lap = ind->lap;
    addr.uap = ind->uap;
    addr.nap = ind->nap;

    MIRROR_LOG("MirrorProfile_ClearBackgroundLeaUnicastConfig lap 0x%04x", addr.lap);

    bkg_unicast_state = MirrorProfile_GetBackgroundUnicastEntryByAddr(&addr);

    if (bkg_unicast_state != NULL)
    {
        if (MirrorProfile_IsBothCisInIdleState(bkg_unicast_state))
        {
            /* Audio config was received earlier, but delegation was never done. This could have
             * happened if primary has sent the audio configs, but delegation request failed or
             * delegation never happened. Clear this entry immediately as this is stale.
             */
            MirrorProfile_ResetUnicastState(bkg_unicast_state);
        }
    }
}

bool  MirrorProfile_SetBackgroundConfigSent(void)
{
    mirror_profile_background_unicast_list_t *bkg_lea_unicast = MirrorProfile_GetBackroundUnicastList();
    bool status = FALSE;

    for (int index = 0; index < MIRROR_PROFILE_BACKGROUND_UNICAST_LIST_MAX_SIZE; index++)
    {
        if (bkg_lea_unicast->config[index].own_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_READY_TO_CONNECT ||
            bkg_lea_unicast->config[index].peer_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_READY_TO_CONNECT)
        {
            bkg_lea_unicast->config[index].config_sent = TRUE;
            status = TRUE;
        }
    }

    return status;
}

/* This Primary EB uses this function to setup a background unicast stream
 * under certain scenario's. For e.g. a secondary EB goes incase and come out.
 */
void MirrorProfile_SendBackgroundAudioConfig(void)
{
    int index;
    mirror_profile_background_unicast_list_t *bkg_lea_unicast = MirrorProfile_GetBackroundUnicastList();

    if (MirrorProfile_IsPrimary() && appPeerSigIsConnected())
    {
        MIRROR_LOG("MirrorProfile_SendBackgroundAudioConfig ");

        for (index = 0; index < MIRROR_PROFILE_BACKGROUND_UNICAST_LIST_MAX_SIZE; index++)
        {
            if (bkg_lea_unicast->config[index].own_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_READY_TO_CONNECT ||
                bkg_lea_unicast->config[index].peer_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_READY_TO_CONNECT)
            {
               if (MirrorProfile_GetState() != MIRROR_PROFILE_STATE_DISCONNECTED && MirrorProfile_IsInSteadyState())
               {
                   /* Mirror is in steady state */
                   bkg_lea_unicast->config[index].config_sent = FALSE;
                   MirrorProfile_SendBackgroundUnicastConfigData(&bkg_lea_unicast->config[index].audio_config);
               }
               else
               {
                   /* Mirror Profile is not in steady state. retry sending background audio config later */
                   MIRROR_LOG("MirrorProfile_SendBackgroundAudioConfig Retry Sending audio config later");
                   MessageCancelFirst(MirrorProfile_GetTask(), MIRROR_PROFILE_INTERNAL_SEND_BACKGROUND_AUDIO_CONFIG);
                   MessageSendLater(MirrorProfile_GetTask(),
                                    MIRROR_PROFILE_INTERNAL_SEND_BACKGROUND_AUDIO_CONFIG,
                                    NULL,
                                    MIRROR_PROFILE_SEND_BACKGROUND_CONFIG_DELAY);
                   return;
               }
            }
        }
    }
}

void MirrorProfile_SetupBackgroundUnicastStream(void)
{
    int index;
    mirror_profile_background_unicast_list_t *bkg_lea_unicast = MirrorProfile_GetBackroundUnicastList();
    bool delegation_req_sent = FALSE;

    if (MirrorProfile_IsPrimary() && appPeerSigIsConnected())
    {
        for (index = 0; index < MIRROR_PROFILE_BACKGROUND_UNICAST_LIST_MAX_SIZE && !delegation_req_sent; index++)
        {
            if (bkg_lea_unicast->config[index].config_sent)
            {
                if (bkg_lea_unicast->config[index].peer_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_READY_TO_CONNECT)
                {
                    MIRROR_LOG("MirrorProfile_SetupBackgroundUnicastStream Delegate Peer Stream %d", bkg_lea_unicast->config[index].peer_cis_handle);

                    mirrorProfile_SetBackgroundCisState(&bkg_lea_unicast->config[index], MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING, FALSE);

                    mirrorProfile_MirrorBackgroundCisCreateReq(&bkg_lea_unicast->config[index],
                                                               bkg_lea_unicast->config[index].peer_cis_handle,
                                                               CIS_DELEGATE_SECONDARY);
                    delegation_req_sent = TRUE;
                }

                if (bkg_lea_unicast->config[index].own_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_READY_TO_CONNECT)
                {
                    MIRROR_LOG("MirrorProfile_SetupBackgroundUnicastStream Delegate Peer Stream %d", bkg_lea_unicast->config[index].own_cis_handle);

                    mirrorProfile_SetBackgroundCisState(&bkg_lea_unicast->config[index], MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING, TRUE);

                    mirrorProfile_MirrorBackgroundCisCreateReq(&bkg_lea_unicast->config[index],
                                                               bkg_lea_unicast->config[index].own_cis_handle,
                                                               CIS_NO_DELEGATE_NO_MIRRORING_SECONDARY);
                    delegation_req_sent = TRUE;
                }
            }
        }
    }
}

bool MirrorProfile_UpdateLeaUnicastSourceFocus(generic_source_t src, bool is_focus_lost)
{
    bool is_initiated;

    assert(MirrorProfile_IsPrimary());

    if (is_focus_lost)
    {
        is_initiated = mirrorProfile_MoveUnicastSourceToBackground(src);
    }
    else
    {
        is_initiated = MirrorProfile_MoveUnicastSourceToForeground(src);
    }

    MIRROR_LOG("MirrorProfile_UpdateLeaUnicastSourceFocus src_type: enum:source_type_t:%d, src: %d, is_focus_lost: %d, is_initiated: %d",
               src.type, src.u.audio, is_focus_lost, is_initiated);

    return is_initiated;
}

generic_source_t MirrorProfile_GetGenericLeSourceIfPresentInBackground(device_t device)
{
    generic_source_t source = {0};

    if (!BtDevice_DeviceIsValid(device))
    {
        source.type = source_type_invalid;
        return source;
    }

    source.type = source_type_audio;
    source.u.audio = DeviceProperties_GetLeAudioSource(device);

    if (MirrorProfile_GetBackgroundUnicastEntryBySource(source) != NULL)
    {
        return source;
    }
    else
    {
        source.type = source_type_voice;
        source.u.voice = DeviceProperties_GetLeVoiceSource(device);

        if (MirrorProfile_GetBackgroundUnicastEntryBySource(source) != NULL)
        {
            return source;
        }
        else
        {
            source.type = source_type_invalid;
        }
    }

    return source;
}

bool MirrorProfile_IsUnicastSessionExistInBackground(gatt_cid_t cid)
{
    return MirrorProfile_GetBackgroundUnicastEntryByCid(cid) != NULL;
}

void MirrorProfile_MoveUnicastSourceToBackgroundInSecondary(void)
{
    mirror_profile_lea_unicast_t *lea_unicast = MirrorProfile_GetLeaUnicastState();
    mirror_profile_lea_unicast_transition_req_t req = {0};

    req.audio_source = lea_unicast->audio_config.audio_source;
    req.voice_source = lea_unicast->audio_config.voice_source;
    req.source_type = lea_unicast->audio_config.audio_source != audio_source_none ? source_type_audio : source_type_voice;
    req.is_foreground = FALSE;

    /* Transition the FG source to Background as ACL create Ind arrived before transition request arrived for the source */
    MirrorProfile_HandleUnicastTransitionReq(&req);
}

/* Handle the Unicast transition request from Primary */
void MirrorProfile_HandleUnicastTransitionReq(const mirror_profile_lea_unicast_transition_req_t *req)
{
    generic_source_t source;
    mirror_profile_lea_unicast_t *unicast_src;
    mirror_profile_lea_unicast_t *lea_unicast = MirrorProfile_GetLeaUnicastState();
    uint8 direction_mask = 0;
    bool transition_done = FALSE;
    uint8 dbg_ignored_at = 0;

    assert(!MirrorProfile_IsPrimary());

    /* Construct the Generic source out of the received request */
    if (req->source_type == source_type_audio)
    {
        source.type = source_type_audio;
        source.u.audio = req->audio_source;
    }
    else
    {
        source.type = source_type_voice;
        source.u.voice = req->voice_source;
    }

    if (req->is_foreground)
    {
        /* The generic source must exist */
        unicast_src = MirrorProfile_GetBackgroundUnicastEntryBySource(source);

        if (unicast_src != NULL)
        {
            /* Soon LEA streaming would start, So power on the Audio SS */
            AudioStartup_ProspectiveOn(audio_startup_mode_async);

            memcpy(lea_unicast, unicast_src, sizeof(mirror_profile_lea_unicast_t));
            lea_unicast->audio_config.is_foreground = TRUE;
            MirrorProfile_ResetUnicastState(unicast_src);

            if (lea_unicast->own_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_CONNECTED)
            {
                /* Transition back to connecting to set up data path */
                lea_unicast->own_cis_state = MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING;
            }

            if (lea_unicast->peer_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_CONNECTED)
            {
                /* Transition back to connecting to set up data path */
                lea_unicast->peer_cis_state = MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING;
            }

            if (MirrorProfile_GetState() != MIRROR_PROFILE_STATE_CIS_CONNECTED)
            {
                MirrorProfile_SetState(MIRROR_PROFILE_STATE_CIS_CONNECTED);
            }

            MirrorProfile_CheckAndEstablishDataPath(lea_unicast);


            transition_done = TRUE;
        }
        else
        {
            dbg_ignored_at = 1;
        }
    }
    else if (lea_unicast->audio_config.audio_source != source.u.audio &&
             lea_unicast->audio_config.voice_source != source.u.voice)
    {
        /* If the source does not exists in foreground ignore */
        dbg_ignored_at = 2;
    }
    else if (MirrorProfile_GetBackgroundUnicastEntryBySource(source) != NULL)
    {
        /* A background source already exists, ignore */
        dbg_ignored_at = 3;
    }
    else
    {
        unicast_src = MirrorProfile_GetNextBackgroundUnicastFreeEntry();
        PanicNull(unicast_src);

        /* Remove data path as this source is moving background */
        if (MirrorProfile_IsOwnCisMirroringOrDelegationDone(lea_unicast))
        {
            direction_mask = ((lea_unicast->own_cis_dir & LE_AUDIO_ISO_DIRECTION_DL) ? (1 << ISOC_DATA_PATH_DIRECTION_CONTROLLER_TO_HOST) : 0)
                           | ((lea_unicast->own_cis_dir & LE_AUDIO_ISO_DIRECTION_UL) ? (1 << ISOC_DATA_PATH_DIRECTION_HOST_TO_CONTROLLER) : 0);
            mirrorProfile_RemoveDataPath(lea_unicast->own_cis_handle, direction_mask);
            lea_unicast->own_cis_state = MIRROR_PROFILE_CIS_SUB_STATE_CONNECTED;
            transition_done = TRUE;
        }

        /* Remove data path as this source is moving background */
        if (MirrorProfile_IsPeerCisMirroringOrDelegationDone(lea_unicast))
        {
            direction_mask = ((lea_unicast->peer_cis_dir & LE_AUDIO_ISO_DIRECTION_DL) ? (1 << ISOC_DATA_PATH_DIRECTION_CONTROLLER_TO_HOST) : 0)
                           | ((lea_unicast->peer_cis_dir & LE_AUDIO_ISO_DIRECTION_UL) ? (1 << ISOC_DATA_PATH_DIRECTION_HOST_TO_CONTROLLER) : 0);
            mirrorProfile_RemoveDataPath(lea_unicast->peer_cis_handle, direction_mask);
            lea_unicast->peer_cis_state = MIRROR_PROFILE_CIS_SUB_STATE_CONNECTED;
            transition_done = TRUE;
        }


        if (transition_done || lea_unicast->config_rcvd)
        {
            /* Inform audio router to remove this source */
            if  (GenericSource_IsAudio(source))
            {
                {
                    MirrorProfile_StopLeAudio();
                }
            }
            else
            {
                {
                    MirrorProfile_StopLeVoice();
                }
            }

            memcpy(unicast_src, lea_unicast, sizeof(mirror_profile_lea_unicast_t));
            unicast_src->audio_config.is_foreground = FALSE;
            MirrorProfile_ResetUnicastState(lea_unicast);

            /* set to disconnected only when the mirror profile state is CIS_CONNECTED */
            if (MirrorProfile_GetState() == MIRROR_PROFILE_STATE_CIS_CONNECTED)
            {
                MirrorProfile_SetState(MIRROR_PROFILE_STATE_DISCONNECTED);
            }
        }
    }

    MIRROR_LOG("MirrorProfile_HandleUnicastTransitionReq is_foreground: %d, transition_done: %d (dbg_ignored_at %d)",
               req->is_foreground, transition_done, dbg_ignored_at);
}

void MirrorProfile_HandleHandoverForBackgroundUnicast(bool is_primary, const tp_bdaddr *tp_bd_addr, gatt_cid_t cid)
{
    mirror_profile_lea_unicast_t* bkg_entry;

    bkg_entry = MirrorProfile_GetBackgroundUnicastEntryByAddr(&tp_bd_addr->taddr.addr);

    MIRROR_LOG("MirrorProfile_HandleHandoverForBackgroundUnicast is_primary: %d CID: 0x%x, bkg_entry: %p", is_primary, cid, bkg_entry);

    if (bkg_entry != NULL)
    {
        bkg_entry->cid = is_primary ? cid: INVALID_CID;

        if (is_primary)
        {
            if (bkg_entry->audio_config.audio_source == audio_source_none)
            {
                bkg_entry->routed_source_type = source_type_voice;
                bkg_entry->routed_source = bkg_entry->audio_config.voice_source;
            }
            else
            {
                bkg_entry->routed_source_type = source_type_audio;
                bkg_entry->routed_source = bkg_entry->audio_config.audio_source;
            }
        }
    }
}

void MirrorProfile_BackgroundUnicastHandlerInit(void)
{
    MirrorProfile_InitBackgroundUnicastSourceDataList();
}

bool MirrorProfile_IsUnicastSourcePresentInBackground(generic_source_t src)
{
    bool present = FALSE;

    if (MirrorProfile_GetBackgroundUnicastEntryBySource(src) != NULL)
    {
        present = TRUE;
    }

    return present;
}

void MirrorProfile_ClearConfigSentForAllBackgroundSessions(void)
{
    int index;
    mirror_profile_background_unicast_list_t *bkg_lea_unicast = MirrorProfile_GetBackroundUnicastList();

    for (index = 0; index < MIRROR_PROFILE_BACKGROUND_UNICAST_LIST_MAX_SIZE; index++)
    {
        bkg_lea_unicast->config[index].config_sent = FALSE;
    }
}

bool MirrorProfile_IsAnyBackgroundSessionInTransientState(void)
{
    int index;
    mirror_profile_background_unicast_list_t *bkg_lea_unicast = MirrorProfile_GetBackroundUnicastList();

    for (index = 0; index < MIRROR_PROFILE_BACKGROUND_UNICAST_LIST_MAX_SIZE; index++)
    {
        if (bkg_lea_unicast->config[index].cid != INVALID_CID &&
            (!MirrorProfile_IsCisInIdleOrConnectedState(bkg_lea_unicast->config[index].peer_cis_state) ||
             !MirrorProfile_IsCisInIdleOrConnectedState(bkg_lea_unicast->config[index].own_cis_state)))
        {
            return TRUE;
        }
    }

    return FALSE;
}

bool MirrorProfile_MergeCisInfoFromBgOnSecondary(mirror_profile_lea_unicast_t *lea_unicast)
{
    bool copied = FALSE;
    mirror_profile_lea_unicast_t *bkg_entry = MirrorProfile_GetBackgroundUnicastEntryByAddr(&lea_unicast->bd_addr);

    /* An entry exists in the background for this address.*/
    if (bkg_entry != NULL)
    {
        if (bkg_entry->peer_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_CONNECTED)
        {
            if (MirrorProfile_GetState() != MIRROR_PROFILE_STATE_CIS_CONNECTED)
            {
                MirrorProfile_SetState(MIRROR_PROFILE_STATE_CIS_CONNECTED);
            }

            lea_unicast->peer_cis_state = MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING;
            lea_unicast->peer_cis_handle = bkg_entry->peer_cis_handle;
            lea_unicast->peer_cis_dir = bkg_entry->peer_cis_dir;
        }

        if (bkg_entry->own_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_CONNECTED)
        {
            if (MirrorProfile_GetState() != MIRROR_PROFILE_STATE_CIS_CONNECTED)
            {
                MirrorProfile_SetState(MIRROR_PROFILE_STATE_CIS_CONNECTED);
            }

            lea_unicast->own_cis_state = MIRROR_PROFILE_CIS_SUB_STATE_CONNECTING;
            lea_unicast->own_cis_handle = bkg_entry->own_cis_handle;
            lea_unicast->own_cis_dir = bkg_entry->own_cis_dir;
        }

        MIRROR_LOG("MirrorProfile_MergeCisInfoFromBgOnSecondary copied from BG with Peer Handle 0x%02x Own Handle 0x%02x",
                    lea_unicast->peer_cis_handle, lea_unicast->own_cis_handle);

        copied = TRUE;
        MirrorProfile_ResetUnicastState(bkg_entry);
    }

    return copied;
}

mirror_profile_state_t  MirrorProfile_EvaluateMirrorProfileStateForUnicastSession(void)
{
    generic_source_t routed_source;
    mirror_profile_state_t next_state = MIRROR_PROFILE_STATE_CIS_CONNECTING;
    mirror_profile_lea_unicast_t *lea_unicast = MirrorProfile_GetLeaUnicastState();

    MIRROR_LOG("MirrorProfile_EvaluateMirrorProfileStateForUnicastSession transition-pending: %d peer enum:mirror_profile_cis_sub_state_t:%d own enum:mirror_profile_cis_sub_state_t:%d",
               lea_unicast->transition_peer_request_pending, lea_unicast->peer_cis_state, lea_unicast->own_cis_state);

    if (lea_unicast->transition_peer_request_pending)
    {
        routed_source.type = lea_unicast->routed_source_type;

        if (routed_source.type == source_type_audio)
        {
           routed_source.u.audio = lea_unicast->routed_source;
        }
        else
        {
           routed_source.u.voice = lea_unicast->routed_source;
        }

        /* Ask the peer to bring the source to foreground */
        MirrorProfile_SendUnicastTransitionReq(routed_source, TRUE);

        lea_unicast->transition_peer_request_pending = FALSE;
    }

    /* This foreground source was copied from background earlier. It could be already
     * stable.Compute the next state.
     */
    switch (lea_unicast->peer_cis_state)
    {
        case MIRROR_PROFILE_CIS_SUB_STATE_IDLE:
        {
            /* Delegation/mirroring process is completed if own cis state is moved connected as peer cis is not avaiable */
            if (lea_unicast->own_cis_state == MIRROR_PROFILE_CIS_SUB_STATE_CONNECTED)
            {
                next_state = MIRROR_PROFILE_STATE_CIS_CONNECTED;
            }
        }
        break;

        case MIRROR_PROFILE_CIS_SUB_STATE_CONNECTED:
        {
            /* Delegation/mirroring process is completed if own cis state is moved connected or own cis is not avaiable */
            if (MirrorProfile_IsCisInIdleOrConnectedState(lea_unicast->own_cis_state))
            {
                next_state = MIRROR_PROFILE_STATE_CIS_CONNECTED;
            }
        }
        break;

        default:
        break;
    }

    return next_state;
}
#else /* INCLUDE_MIRRORING && ENABLE_LE_MULTIPOINT_BACKGROUND_CIS_SUPPORT */
/* Fallback implementation */

bool MirrorProfile_UpdateLeaUnicastSourceFocus(generic_source_t src, bool is_focus_lost)
{
    UNUSED(src);
	UNUSED(is_focus_lost);
	return FALSE;
}

bool MirrorProfile_IsUnicastSessionExistInBackground(gatt_cid_t cid)
{
	UNUSED(cid);
    return FALSE;
}

#endif /* INCLUDE_MIRRORING && ENABLE_LE_MULTIPOINT_BACKGROUND_CIS_SUPPORT */

